package com.sff.module {
	
	import com.sff.core.IFont;
	import com.sff.core.ILoadable;
	import com.sff.core.sff_internal;
	import com.sff.events.LoaderErrorEvent;
	import com.sff.events.LoaderEvent;
	import com.sff.events.ModuleEvent;
	import com.sff.manager.DataManager;
	import com.sff.manager.LoaderManager;
	import com.sff.manager.StyleManager;
	import com.sff.net.LoaderItem;
	import com.sff.net.URLLoaderItem;
	import com.sff.security.Security;
	
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.net.URLRequest;
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;
	import flash.text.Font;
	import com.sff.system.ModuleSystem;

	public class ModuleLoader extends LoaderManager {

		public static const globalDomain 	: ApplicationDomain = new ApplicationDomain( ApplicationDomain.currentDomain );

		protected var _parentData 			: DataManager;
		protected var _parentModule 		: Module;
		protected var _module 				: Module;
		protected var _data 				: DataManager;
		protected var _style				: StyleManager;
		
		protected var _isLoading 			: Boolean = false;
		protected var _addedToBatch 		: Boolean = false;		
		
		private var _gcEligible 			: Boolean = false;
		
		public function ModuleLoader(parentModule : Module, priority : Number = 0 ) {
			_parentModule = parentModule;
			
			addEventListener( LoaderEvent.ADDED , onAdded );
			addEventListener( Event.COMPLETE , onCompleteA, false, 1000 );
			addEventListener( Event.COMPLETE , onCompleteB, false, -10 );
			addEventListener( ErrorEvent.ERROR , onError );
		}
		
		override public function dispose() : void {
			
			if( _gcEligible ) return;
			
			removeEventListener( Event.COMPLETE , onCompleteB );
			removeEventListener( Event.COMPLETE , onCompleteA );
			removeEventListener( ErrorEvent.ERROR , onError );
			
			if( _module && _isLoading ) {
				_gcEligible = true;
				_module.cleanUp();
				_module = null;
			}
			
			super.dispose( );
		}
		
		
		private function onError(evt: ErrorEvent) : void {
			dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
		}
		
		
		public function get module() : Module {
			return _isLoading ? null : _module;
		}
		
		//public function set channel( val : Number ) : void {
			//sff_internal::_batchableGroup.setOffset( val );
		//}
		
		public function load(request : URLRequest, parent : DataManager = null) : void {
			if( _gcEligible ) return;
			
			if( request.url == "" ) {
				//dispatchEvent( new ModuleEvent( ModuleEvent.) );
				return;
			}
			
			if( parent ) {
				_parentData = parent;
			}
			
			_isLoading = true;
			
			var ldr:URLLoaderItem = new URLLoaderItem(request);
			ldr.addEventListener(Event.COMPLETE, onDataCompleteHandler);
			
			addItem(ldr);
			
			if (_run == false) {
				execute();
			}
		}
		
		protected function onDataCompleteHandler(evt: Event) : void {
			var ldr : URLLoaderItem = evt.target as URLLoaderItem;
			ldr.removeEventListener( Event.COMPLETE , onDataCompleteHandler);
			
			if(ldr.data == undefined ) return;
			
			parseData( XML(ldr.data ) );
		}
		
		public function parseData( xml : XML ) : void {
			_data = new DataManager( xml, _parentModule.system.dataManager );			
			_style = new StyleManager();
			
			loadStatic();
		}
		
		private function loadStatic() : void {			
			if (_data.staticURL != null && _data.staticURL != '') {				
				var request:URLRequest = new URLRequest(_data.staticURL);
				
				var ldr:URLLoaderItem = new URLLoaderItem(request);
				ldr.addEventListener(Event.COMPLETE, onStaticCompleteHandler);
				
				addItem(ldr);
				
			} else {				
				loadModule();
			}
		}
		
		private function onStaticCompleteHandler(evt : Event) : void {
			evt.currentTarget.removeEventListener(Event.COMPLETE, onStaticCompleteHandler);
			
			_data.sff_internal::parseStatic(XML(evt.currentTarget.data));
			
			//css styles
			if (_data.styles.length) {				
				var urlLdr:URLLoaderItem;
				for each (var url:String in _data.styles) {					
					urlLdr = new URLLoaderItem(new URLRequest(url));
					urlLdr.addEventListener(Event.COMPLETE, onCompleteLoadStyleHandler);
					
					addItem(urlLdr);
				}				
			}
			
			//fonts
			if (_data.fonts.length) {				
				var ldr:LoaderItem;
				for each (var font:String in _data.fonts) {					
					ldr = new LoaderItem(new URLRequest(font));
					ldr.addEventListener(Event.COMPLETE, onCompleteLoadFontHandler);
					
					addItem(ldr);
				}				
			}
		
			loadModule();
		}
		
		private function onCompleteLoadStyleHandler(evt:Event):void {
			evt.currentTarget.removeEventListener(Event.COMPLETE, onCompleteLoadStyleHandler);
			
			_style.parseCssStyleSheet(evt.currentTarget.data.toString());			
		}
		
		private function onCompleteLoadFontHandler(evt:Event):void {
			evt.currentTarget.removeEventListener(Event.COMPLETE, onCompleteLoadFontHandler);
			
			_style.addFont(evt.currentTarget.loader.content as IFont);
		}
		
		private function loadModule() : void {
			
			//should load module after css style and font are loaded
			var context:LoaderContext = new LoaderContext( false , new ApplicationDomain( ModuleLoader.globalDomain ), Security.instance.sandbox );		
			var request:URLRequest = new URLRequest(_data.moduleURL);		
			
			var ldr:LoaderItem = new LoaderItem(request, context, null, 'Loading Module');			
			ldr.addEventListener( Event.COMPLETE , onModuleLoadedHandler , false, 100 );
			
			addItem(ldr);
		}
		
		protected function onModuleLoadedHandler(evt: Event ) : void {
			var ldr: LoaderItem = evt.currentTarget as LoaderItem;
			ldr.removeEventListener( Event.COMPLETE , onModuleLoadedHandler );
			
			_module = ldr.loader.content as Module;
			
			//if( _module == null ) 
						
			var system:ModuleSystem = new ModuleSystem(_data, _style, _module);
			_module.sff_internal::_init(system);
			
			dispatchEvent( new ModuleEvent( ModuleEvent.LOADED, _module ) );
			
			//load neccessary material of module before show module
			var mLoadables: ILoadable = _module.getLoadables();
			if(mLoadables) {
				addItem(mLoadables);
			}
		}
		
		protected function onCompleteA(event : Event) : void {
			removeEventListener( Event.COMPLETE , onCompleteA );
			_isLoading = false;
		}
		
		protected function onCompleteB(event : Event) : void {
			removeEventListener( Event.COMPLETE , onCompleteB );
			_parentModule = null;
		}
		
		protected function onAdded(event : LoaderEvent) : void {
			addEventListener( LoaderEvent.REMOVED , onRemoved );
			removeEventListener( LoaderEvent.ADDED , onAdded );
			_addedToBatch = true;
		}
		
		protected function onRemoved(event : LoaderEvent) : void {
			removeEventListener( LoaderEvent.REMOVED , onRemoved );
			addEventListener( LoaderEvent.ADDED , onAdded );
			_addedToBatch = false;
		}
	}
}